/**
 * 
 */
package net.toocruel.iqismart.service.core.impl;


import net.sf.json.JSONArray;
import net.sf.json.JSONObject;
import net.toocruel.iqismart.dao.UserRepository;
import net.toocruel.iqismart.dao.condition.QuestionsCondition;
import net.toocruel.iqismart.dao.core.*;
import net.toocruel.iqismart.entity.common.Category;
import net.toocruel.iqismart.entity.common.Tag;
import net.toocruel.iqismart.entity.constants.Action;
import net.toocruel.iqismart.entity.constants.NotificationType;
import net.toocruel.iqismart.entity.core.*;
import net.toocruel.iqismart.entity.core.Collection;
import net.toocruel.iqismart.entity.rbac.User;
import net.toocruel.iqismart.entity.setting.Setting;
import net.toocruel.iqismart.security.core.support.Result;
import net.toocruel.iqismart.service.UserService;
import net.toocruel.iqismart.service.core.EmailService;
import net.toocruel.iqismart.service.core.NotificationService;
import net.toocruel.iqismart.service.core.QuestionService;
import net.toocruel.iqismart.service.core.TagService;
import net.toocruel.iqismart.service.exception.QuestionNotFoundException;
import net.toocruel.iqismart.service.helper.UserHelper;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Sort;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.thymeleaf.TemplateEngine;
import org.thymeleaf.context.Context;

import javax.mail.MessagingException;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Root;
import java.util.*;

/**
 * @author toocruel
 *
 */
@Service
@Transactional
public class QuestionServiceImpl implements QuestionService {
    @Autowired
    private UserRepository userRepository;
    @Autowired
    private TagRepository tagRepository;

    @Autowired
    private QuestionRepository questionRepository;

    @Autowired
    private AttentionRepository attentionRepository;

    @Autowired
    private CollectionRepository collectionRepository;

    @Autowired
    private TagService tagService;

    @Autowired
    private CategoryRepository categoryRepository;

    @Autowired
    private AnswerRepository answerRepository;

    @Autowired
    private QuestionInvitationRepository questionInvitationRepository;

    @Autowired
    private EmailService emailService;

    @Autowired
    private UserService userService;

    @Override
    public Question postQuestion(Question question) {
        //标签
        question.setTags(tagService.findAndCreateIfNotExist(question.getCategoryId(),question.getTagNames()));

        if(question.getCategoryId()!= null && question.getCategoryId()>0){
            question.setCategory(categoryRepository.findOne(question.getCategoryId()));
        }

        //状态 1 审核通过
        question.setStatus(1);
        question = questionRepository.save(question);

        //积分奖励
        userService.addCredit(new Credit(question.getUser(),Action.CREATE_QUESTION,question));

        return question;
    }

    @Override
    public Question editQuestion(Question question) {
        Question _question = questionRepository.findOne(question.getId());

        //标题 描述
        _question.setTitle(question.getTitle());
        _question.setDescription(question.getDescription());

        //分类
        if(question.getCategoryId()!= null && question.getCategoryId()>0){
            _question.setCategory(categoryRepository.findOne(question.getCategoryId()));
        }

        //标签
        _question.setTags(tagService.findAndCreateIfNotExist(question.getCategoryId(), question.getTagNames()));

        questionRepository.save(_question);
        return _question;
    }



    @Override
    public Question findById(Long id) {
        return questionRepository.findOne(id);
    }

    @Override
    public Question findById(Long id,User user) {
        Question question = this.findById(id);

        if(question == null){
            throw new QuestionNotFoundException();
        }

        if(user == null){
            return question;
        }

        Attention attention = attentionRepository.findOne(new Specification<Attention>() {
            @Override
            public Predicate toPredicate(Root<Attention> root, CriteriaQuery<?> query, CriteriaBuilder cb) {
                return cb.and(cb.equal(root.get("question"), question), cb.equal(root.get("user"), user.getId()));
            }
        });
        if(attention!=null){
            question.setAttented(true);
        }


        Collection collection = collectionRepository.findOne(new Specification<Collection>() {
            @Override
            public Predicate toPredicate(Root<Collection> root, CriteriaQuery<?> query, CriteriaBuilder cb) {
                return cb.and(cb.equal(root.get("question"), question), cb.equal(root.get("user"), user.getId()));
            }
        });
        if(collection!=null){
            question.setCollected(true);
        }

        question.setAnswered(false);
        question.getAnswers().forEach(answer -> {
            if(answer.getUser() == user){
                question.setAnswered(true);
            }
        });


        return question;
    }

    @Override
    public List<Question> getSuggests(String word) {
        return questionRepository.findAll();
    }



    @Override
    public Result getRecommentInviteUsers(Long questionId, String word) {
        Result re = new Result();
        List<JSONObject> list = new ArrayList<>();

        //先按问题的标签匹配回答过含有这些标签的问题的用户
        Question question = questionRepository.findOne(questionId);

        Map<Integer,JSONObject> temp = new HashMap<>();
        Set<Tag> tags = question.getTags();
        if(!CollectionUtils.isEmpty(tags)){
            List<JSONObject> recommentInviteUsers = new ArrayList<>();
            questionRepository.getRecommentInviteUsers(questionId).forEach(objects -> {
                JSONArray array = JSONArray.fromObject(objects);
                JSONObject jsonObject = new JSONObject();
                jsonObject.put("id",Long.parseLong(array.get(0)+""));
                jsonObject.put("username",String.valueOf(array.get(1)));
                jsonObject.put("tagName",String.valueOf(array.get(2)));
                jsonObject.put("tagAnswers",Integer.parseInt(array.get(3)+""));
                JSONObject tempJSON = temp.get(jsonObject.optInt("id"));

                if(tempJSON!=null){
                    tempJSON.put("tagName",tempJSON.optString("tagName")+","+jsonObject.optString("tagName"));
                    tempJSON.put("tagAnswers",tempJSON.optInt("tagAnswers")+jsonObject.optInt("tagAnswers"));
                }else{
                    temp.put(jsonObject.optInt("id"),jsonObject);
                }
            });

            temp.values().forEach(jsonObject -> {
                if(StringUtils.isBlank(word) || jsonObject.optString("username").contains(word)){
                    if(jsonObject.optInt("id") != question.getUser().getId() ){
                        if(UserHelper.getCurrentUser()!=null && jsonObject.optInt("id") == UserHelper.getCurrentUser().getId()){
                        }else{
                            recommentInviteUsers.add(jsonObject);
                        }
                    }
                }
            });

            list.addAll(recommentInviteUsers);

            list.sort((o1, o2) -> o1.optInt("id") - o2.optInt("id"));

        }


        list.forEach(jsonObject -> {
            jsonObject.put("url","/space/"+jsonObject.optInt("id"));
            jsonObject.put("avatar","/image/avatar/"+jsonObject.optInt("id"));
//            answerRepository.findByQUestionAndUser(question,jsonObject.optInt("id"));
            jsonObject.put("isInvited",0);
            QuestionInvitation invitation = questionInvitationRepository.findOne((root, query, cb) -> {
                return cb.and(cb.equal(root.get("question"), question),
                        cb.equal(root.get("fromUser"), UserHelper.getCurrentUser()),
                        cb.equal(root.get("toUser"), jsonObject.optLong("id"))
                );
            });
            if(invitation!=null){
                jsonObject.put("isInvited",1);
            }
        });

        re.setCode(list.size()>0?0:-1);
        re.setMessage(list.size()>0?list:"noData");
        return re;
    }

    @Autowired
    private NotificationService notificationService;
    @Override
    public Result inviteUser(Long questionId, Long userId) throws MessagingException {

        Question question = questionRepository.findOne(questionId);

        Result re = new Result();
        List<JSONObject> list = new ArrayList<>();
        QuestionInvitation invitation = new QuestionInvitation();
        invitation.setQuestion(question);
        invitation.setFromUser(UserHelper.getCurrentUser());
        User toUser = userRepository.findById(userId);
        invitation.setToUser(toUser);
        invitation.setStatus(1);
        questionInvitationRepository.save(invitation);

        if(StringUtils.isNotBlank(toUser.getEmail())){
            sendInviteEmail(toUser.getEmail(), question);
        }

        if(toUser.getSiteNotifications().contains(NotificationType.INVITE_ANSWER)){
            Notification notification = new Notification();
            notification.setToUser(toUser);
            notification.setFromUser(userRepository.findOne(UserHelper.getCurrentUser().getId()));
            notification.setQuestion(question);
            notificationService.send(notification);
        }
        if(toUser.getEmailNotifications().contains(NotificationType.INVITE_ANSWER)){
            //TODO 发送邀请回答邮件
        }

        re.setCode(0);
        re.setMessage("已邀请");
        return re;
    }


    @Autowired
    private TemplateEngine templateEngine;



    @Override
    public Result inviteEmail(Long questionId, String email) throws MessagingException {

        Question question = questionRepository.findOne(questionId);

        Result re = new Result();
        List<JSONObject> list = new ArrayList<>();
        QuestionInvitation invitation = new QuestionInvitation();
        invitation.setQuestion(question);
        invitation.setFromUser(UserHelper.getCurrentUser());
        invitation.setToEmail(email);
        invitation.setStatus(1);
        questionInvitationRepository.save(invitation);

        sendInviteEmail(email, question);

        re.setCode(0);
        re.setMessage("已邀请");
        return re;
    }

    private void sendInviteEmail(String email, Question question) throws MessagingException {
        //构造上下文(Model)
        Context context = new Context();
        User user = new User();
        user.setUsername("某人");
        if (UserHelper.getCurrentUser() != null) {
            user =  UserHelper.getCurrentUser();
        }
        context.setVariable("user", user);
        context.setVariable("question", question);
        context.setVariable("GlobalSetting", Setting.SETTING);

        //渲染模板
        String html = templateEngine.process("emails/invite_answer", context);
        emailService.sendHtml(email,user.getUsername()+"正在向你发问题求助："+question.getTitle(),html);
        System.out.println("");
    }

    @Override
    public Page<QuestionInvitation> getInvitations(Long id) {
        PageRequest pageRequest = new PageRequest(0, 20, new Sort(Sort.Direction.DESC, "id"));
        return questionInvitationRepository.findAll((root, query, cb) -> cb.and(cb.equal(root.get("question"),id)),pageRequest);
    }

    @Override
    public Page<Question> findAll(QuestionsCondition condition, PageRequest pageRequest) {

        String categorySlug = condition.getCategorySlug();
        Long categoryId = condition.getCategoryId();
        String filter = condition.getFilter();

        if(StringUtils.isNotBlank(filter)){
            //newest hottest reward unAnswered
            if("newest".equals(filter)){
                pageRequest = new PageRequest(pageRequest.getPageNumber(),pageRequest.getPageSize(),new Sort(Sort.Direction.DESC,"id"));
            }else if("newanswer".equals(filter)){
                pageRequest = new PageRequest(pageRequest.getPageNumber(),pageRequest.getPageSize(),new Sort(Sort.Direction.DESC,"lastAnswer").and(new Sort(Sort.Direction.DESC,"id")));
            }else if("mostview".equals(filter)){
                pageRequest = new PageRequest(pageRequest.getPageNumber(),pageRequest.getPageSize(),new Sort(Sort.Direction.DESC,"views").and(new Sort(Sort.Direction.DESC,"id")));
            }else if("mostanswer".equals(filter)){
                pageRequest = new PageRequest(pageRequest.getPageNumber(),pageRequest.getPageSize(),new Sort(Sort.Direction.DESC,"answerNum").and(new Sort(Sort.Direction.DESC,"id")));
            }else if("hottest".equals(filter)){
                pageRequest = new PageRequest(pageRequest.getPageNumber(),pageRequest.getPageSize(),new Sort(Sort.Direction.DESC,"views").and(new Sort(Sort.Direction.DESC,"id")));
            }else if("reward".equals(filter)){
                pageRequest = new PageRequest(pageRequest.getPageNumber(),pageRequest.getPageSize(),new Sort(Sort.Direction.DESC,"views").and(new Sort(Sort.Direction.DESC,"id")));
            }else if("unAnswered".equals(filter)){
                pageRequest = new PageRequest(pageRequest.getPageNumber(),pageRequest.getPageSize(),new Sort(Sort.Direction.ASC,"status").and(new Sort(Sort.Direction.DESC,"id")));
            }else if("resolved".equals(filter)){
                pageRequest = new PageRequest(pageRequest.getPageNumber(),pageRequest.getPageSize(),new Sort(Sort.Direction.ASC,"status").and(new Sort(Sort.Direction.DESC,"id")));
            }
        }

        Page<Question> questions = questionRepository.findAll((root, query, cb) -> {
            List<Predicate> predicates = new ArrayList<>();
            if (categoryId != null && categoryId > 0) {
                Category category = categoryRepository.findOne(categoryId);
                condition.setCategory(category);
                predicates.add(cb.equal(root.get("category"), category));
            }

            if (StringUtils.isNotBlank(categorySlug)) {
                Category category = categoryRepository.findBySlug(categorySlug);
                condition.setCategory(category);
                predicates.add(cb.equal(root.get("category"), category));
            }

            if(StringUtils.isNotBlank(filter)){
                //newest hottest reward unAnswered
                if("newest".equals(filter)){
                }else if("hottest".equals(filter)){
                }else if("reward".equals(filter)){
                    predicates.add(cb.greaterThan(root.get("price"),0));
                }else if("unAnswered".equals(filter)){
                    predicates.add(cb.isEmpty(root.get("answers")));
                }else if("resolved".equals(filter)){
                    predicates.add(cb.equal(root.get("status"),2));
                }
            }



            //TODO

            return cb.and(predicates.toArray(new Predicate[predicates.size()]));
        }, pageRequest);

        syncQuestionNum(questions);


        return questions;
    }

    @Override
    public void addViews(Question question) {
        question.setViews(question.getViews()+1);
        questionRepository.save(question);
    }

    private void syncQuestionNum(Iterable<Question> questions){
        questions.forEach(question -> {
            try {
                if(question.getSyncedAt()!= null && System.currentTimeMillis() - question.getSyncedAt()<1000 *60 * 60 * 24  ){
                    return;
                }
                if(CollectionUtils.isNotEmpty(question.getAnswers())){
                    if(question.getAnswerNum() != question.getAnswers().size()){
                        question.setAnswerNum(question.getAnswers().size());
                        question.setSyncedAt(System.currentTimeMillis());
                    }
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        });

        questionRepository.save(questions);

    }

    @Override
    public List<Question> getLatestQuestions(int count) {
        return questionRepository.findAll(new PageRequest(0,count,new Sort(Sort.Direction.DESC,"id"))).getContent();
    }

    @Override
    public List<Question> getLatestRewardQuestions(int count) {
        return questionRepository.findAll((root, query, cb) -> cb.greaterThan(root.get("price"),0),new PageRequest(0,count,new Sort(Sort.Direction.DESC,"id"))).getContent();
    }
}
